##@file geo.tcl
# Geometric commands
# \verbatim

source cons.tcl

##@private Documented oo method @c oo_method_x 
##move the object by (+x,+y,+z)
oo::define Charmm method move { object args } {
  set selection [my selectSegs $object]
  set com [my getCOM $object]
  array set option $args
  #determine translation vector
  if ![info exists option(-x)] { set x 0} else {set x [expr double($option(-x))]}
    if ![info exists option(-y)]  { set y 0} else {set y [expr double($option(-y))]}
      if ![info exists option(-z)] { set z 0} else {set z [expr double($option(-z))]}
  set length [expr $x*$x+$y*$y+$z*$z]
  set cmd "coor translate $selection -\n xdir [expr $x/$length] ydir [expr $y/$length] zdir [expr $z/$length] -\n dist $length"
  my sendCmd $cmd

}
##move the object to (x,y,z)
oo::define Charmm method move2 { object args} {
  set selection [my selectSegs $object]
  set com [my getCOM $object]
  array set option $args
  #determine translation vector
  if [info exists option(-x)] { set x [expr $option(-x)-[lindex $com 0]]} else { set x 0}
  if [info exists option(-y)] { set y [expr $option(-y)-[lindex $com 1]]} else { set y 0}
  if [info exists option(-z)] { set z [expr $option(-z)-[lindex $com 2]]} else { set z 0}
  set length [expr sqrt($x*$x+$y*$y+$z*$z)]
  set cmd "coor translate $selection -\n xdir [expr $x/$length] ydir [expr $y/$length] zdir [expr $z/$length] -\n dist $length"
  my sendCmd $cmd
}

oo::define Charmm method align { object args} {

}

oo::define Charmm method tilt { object args } {
   
}

# rotate object around an axis or its principle axis
oo::define Charmm method rotate2 {object args} {
  array set option $args
  set orient [my getOrientation $object]
  if [info exists option(-tilt)] { set tilt [expr $option(-tilt)-[lindex $orient 0]]} else { set tilt 0}
  if [info exists option(-phi)] { set phi [expr [lindex $orient 1]- $option(-phi)]} else { set phi 0}
  if [info exists option(-rotation)] { set rotation [expr $option(-rotation)-[lindex $orient 2]]} else { set rotation 0}
  my rotate $object -tilt $tilt -phi $phi -rotation $rotation
}

oo::define Charmm method rotate {object args} {
  set selection [my selectSegs $object]
  set com [my getCOM $object]
  array set option $args
  if [info exists option(-tilt)] { set tilt $option(-tilt)} else { set tilt 0}
  if [info exists option(-phi)] { set phi  $option(-phi)} else { set phi 0}
  if [info exists option(-rotation)] { set rotation $option(-rotation)} else { set rotation 0}
  
  # rotate tilt
  #rotate around the product of z axis and principle axis.
  set output [my sendCmd "coor lsqp major $selection"]
  set major [my getReplacable ?XAXI ?YAXI ?ZAXI]  
  my sendCmd "coor rotate $selection -\n xcen [lindex $com 0] ycen [lindex $com 1] zcen [lindex $com 2] -\n xdir [lindex $major 1] ydir [expr -[lindex $major 0]] zdir 0.0 phi $tilt"
  
  #rotate phi
  #rotation not correct? 0+20->70
    my sendCmd "coor rotate $selection -\n xcen [lindex $com 0] ycen [lindex $com 1] zcen [lindex $com 2] -\n xdir 0 ydir 0 zdir 1 phi $phi"
  #rotate rotation  
  set output [my sendCmd "coor lsqp major $selection"]
  set major [my getReplacable ?XAXI ?YAXI ?ZAXI]  
  my sendCmd "coor rotate $selection xcen [lindex $com 0] ycen [lindex $com 1] zcen [lindex $com 2] -\n xdir [lindex $major 0] ydir [lindex $major 1] zdir [lindex $major 2] phi $rotation"
}
oo::define Charmm method getCOM { object } {
  set selection [my selectSegs $object]
  set output [my sendCmd "coor stat mass $selection"] 
  set output [regexp -all -inline -- {[^\s:=\n]+} $output]
  set idx_start [lsearch $output XMIN]
  set idx_end [ expr 1+[lsearch $output WAVE]]
  array set coor [lrange $output $idx_start $idx_end]
#  parray coor
  return [list $coor(XAVE) $coor(YAVE) $coor(ZAVE)]
}

oo::define Charmm method getOrientation { object } {
  set selection [my selectSegs $object]
   set output [my sendCmd "coor lsqp norm $selection "]
   set norm [my getReplacable ?XAXI ?YAXI ?ZAXI]
   set output [my sendCmd "coor lsqp major $selection"]
   set major [my getReplacable ?XAXI ?YAXI ?ZAXI]
   set output [my sendCmd "coor lsqp minor $selection"]
   set minors [my getReplacable ?XAXI ?YAXI ?ZAXI]
      #calc  angle... need to 
   set tilt [ expr acos([lindex $major 2])]
   set x [lindex $major 0]
   set y [lindex $major 1]
   set phi [ expr asin($y/sqrt($x*$x+$y*$y))]
   set x [expr -sin($phi)*cos($tilt)]
   set y [expr -cos($phi)*cos($tilt)]
   set z [expr sin($tilt)]
   set rotation [expr $x*[lindex $norm 0]+$y*[lindex $norm 1]+$z*[lindex $norm 2]]
   if {$rotation > 1.00} {set rotation 1.000}
   set rotation [expr acos($rotation)]
   set tilt [expr 180*$tilt/3.1415926]
   set phi [expr 180*$phi/3.1415926]
   set rotation [expr 180*$rotation/3.1415926]
   return [list $tilt $phi $rotation]
}

#Helper

oo::define Charmm method selectSegs { segments } {
    if {[string compare $segments  all ] ==0 } { set str all } else {
     set str ""
     foreach segment $segments {
       append str " segid $segment .or. "
      }
       set str [string range $str 0 end-5]
     }
    
    return "sele $str end"
}
oo::define Charmm method getReplacable {args} {
  set output [my sendCmd "set result $args"]
  set output [regexp -all -inline -- {\"(.*?)\"} $output]
   set values {}
   foreach var $args {
     set idx [lsearch $output $var]
     set Vars($var) [lindex $output [expr $idx +2]]
     lappend values [lindex $output [expr $idx +2]]
   }
#   parray Vars
  return $values
}

